#include "Frame.h"
#include "Strategy.h"
#include "Log.h"

#ifdef _WIN32
#include <windows.h>
#include <vector>
#include <time.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <time.h>
#include <string.h>
#endif

int iSnakeBody = 0;
using namespace std;


CFrame::CFrame() :m_pStrategy(NULL)
{
}


CFrame::~CFrame()
{
}

bool CFrame::Init(CBaseStrategy *pStrategy)
{
    const int iType[40][40] =
    {
        { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
        { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 }
    };
    m_FrameData.stMap.iOffsetX = 0;
    m_FrameData.stMap.iOffsetY = 0;
    m_FrameData.stMap.iHight = 20;
    m_FrameData.stMap.iWidth = 20;

    m_FrameData.stSnake.stBody[0].iX = 1;
    m_FrameData.stSnake.stBody[0].iY = 3;
    m_FrameData.stSnake.stBody[1].iX = 1;
    m_FrameData.stSnake.stBody[1].iY = 2;
    m_FrameData.stSnake.stBody[2].iX = 1;
    m_FrameData.stSnake.stBody[2].iY = 1;

    m_FrameData.stSnake.iLength = 3;

    int iHight = GetMapHight();
    int iWidth = GetMapWidth();
    for (int iHightTmp = 0; iHightTmp < iHight; ++iHightTmp)
    {
        for (int iWidthTmp = 0; iWidthTmp < iWidth; ++iWidthTmp)
        {
            MapUnitData &stMapUnitData = MutableMapUnitData(iWidthTmp, iHightTmp);
            stMapUnitData.eType = (EMapUnitType)(iType[iHightTmp][iWidthTmp]);
        }
    }

    if (pStrategy == NULL)
    {
        return false;
    }

    m_pStrategy = pStrategy;

    const int iSnakePos[4][6] =
    {
        { 1,3,1,2,1,1 },
        { 3,1,4,1,5,1 },
        { 5,3,5,4,5,5 },
        { 3,5,2,5,1,5 },
    };
    vector<Pos> vecSnakePos[4];
    for (int i = 0; i < 4; ++i)
    {
        for (int j = 0; j < 3;++j)
        {
            Pos stPos;
            stPos.iX = iSnakePos[i][j * 2];
            stPos.iY = iSnakePos[i][j * 2 + 1];
            vecSnakePos[i].push_back(stPos);
        }
    }

    
    static CSnake cSnakeA(this);
    static CStrategy cStrategyA(*this, cSnakeA);
    cSnakeA.SetStrategy(&cStrategyA);
    cSnakeA.Init(vecSnakePos[0]);
    AddSnake(&cSnakeA);

    static CSnake cSnakeB(this);
    static CStrategy cStrategyB(*this, cSnakeB);
    cSnakeB.SetStrategy(&cStrategyB);
    cSnakeB.Init(vecSnakePos[1]);
    AddSnake(&cSnakeB);

    static CSnake cSnakeC(this);
    static CStrategy cStrategyC(*this, cSnakeC);
    cSnakeC.SetStrategy(&cStrategyC);
    cSnakeC.Init(vecSnakePos[2]);
    AddSnake(&cSnakeC);

    static CSnake cSnakeD(this);
    static CStrategy cStrategyD(*this, cSnakeD);
    cSnakeD.SetStrategy(&cStrategyD);
    cSnakeD.Init(vecSnakePos[3]);
    AddSnake(&cSnakeD);


    srand((unsigned int)time(NULL));
    return true;
}

int CFrame::Run()
{
     
#ifndef _WIN32
    printf("\033[2J");
    printf("\033[?25l");
#endif

    srand((unsigned int)time(NULL));
    CreateInterface();
    if (!CreateOneFoodRand())
    {
        printf("fail to create food.");
        return 0;
    }

    if (m_pStrategy == NULL)
    {
        printf("call Init(pStrategy) first");
        return 0;
    }

    while (m_vecpSnake.size())
    {
        for (int iSnakeCountTmp = 0; iSnakeCountTmp < (int)m_vecpSnake.size(); )
        {
            CSnake *pSnake = m_vecpSnake[iSnakeCountTmp];
            
            if (pSnake == NULL)
            {
                m_vecpSnake.erase(m_vecpSnake.begin() + iSnakeCountTmp);
            }

            EDirType eDir = pSnake->MoveSnake();
            EMapUnitType eMapUintType = MoveSnake(eDir, *pSnake);
            switch (eMapUintType)
            {
            case EM_MAP_UNIT_TYPE_WALL:
            case EM_MAP_UNIT_TYPE_SNACKE:
            case EM_MAP_UNIT_TYPE_MAX:
                m_vecpSnake.erase(m_vecpSnake.begin() + iSnakeCountTmp);
                continue;
            default:
                break;
            }

            CreateOneFoodRand();
            ++iSnakeCountTmp;
        }
#ifdef _WIN32
        Sleep(100);
#else
        usleep(100*1000);
#endif
    }
     
#ifndef _WIN32
    printf("\033[?25l");
#endif

    return 0;
}


void CFrame::AddSnake(CSnake *pSnake)
{
    if (pSnake == NULL)
    {
        return;
    }

    const Pos *pSnakeBody = pSnake->GetSnake();
    if (pSnakeBody == NULL)
    {
        return;
    }
 
    //判断是否可以加入蛇
    int iSnakeLength = pSnake->GetSnakeLength();

    for (int iLengthTmp = 0; iLengthTmp < iSnakeLength; ++iLengthTmp)
    {
        const Pos &stSnakeBody = pSnakeBody[iLengthTmp];
        const MapUnitData &stMapUnitData = GetMapUnitData(stSnakeBody.iX, stSnakeBody.iY);
        if (stMapUnitData.eType != EM_MAP_UNIT_TYPE_NULL)
        {
            return;
        }
    }

    //绘制蛇，并加入
    for (int iLengthTmp = 0; iLengthTmp < iSnakeLength; ++iLengthTmp)
    {
        const Pos &stBodyPos = pSnakeBody[iLengthTmp];
        MapUnitData &stMapUnitData = MutableMapUnitData(stBodyPos.iX, stBodyPos.iY);
        stMapUnitData.eType = EM_MAP_UNIT_TYPE_SNACKE;

        SetPosition(stBodyPos.iX, stBodyPos.iY);
        Print(UIChar[EM_MAP_UNIT_TYPE_SNACKE]);
    }

    m_vecpSnake.push_back(pSnake);
}

void CFrame::SetPosition(int x, int y)
{
#ifdef _WIN32
    COORD pos;
    HANDLE hOutput;
    pos.X = (GetMapOffsetX() + x) * 2;
    pos.Y = GetMapOffsetY() + y;
    hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hOutput, pos);
#else

    int iX = (GetMapOffsetX() + x) * 2 + 1;
    int iY = GetMapOffsetY() + y + 1;
    printf("\033[%d;%dH", iY, iX);

#ifndef _WIN32
    fflush(stdout);
#endif

#endif
}
void CFrame::CreateInterface()
{
    int iHight = GetMapHight();
    int iWidth = GetMapWidth();
    
    for (int iHightTmp = 0; iHightTmp < iHight; ++iHightTmp)
    {
        SetPosition(0, iHightTmp);
        for (int iWidthTmp = 0; iWidthTmp < iWidth; ++iWidthTmp)
        {
            const MapUnitData &stUnitData = GetMapUnitData(iWidthTmp, iHightTmp);
            Print(UIChar[stUnitData.eType]);
        }
    }
}

//循环随机五次，如果五次都失败了，就搜集地图上的空点，在空点中随机食物
bool CFrame::CreateOneFoodRand()
{
    bool bCreateFood = false;

    const int iWidth = GetMapWidth();
    const int iHight = GetMapHight();

    Pos stNewFoodPos;

    const Pos &stHead = GetSnakeHead();

    //循环随机五次
    for (int iRound = 0; iRound < 5; ++iRound)
    {
        stNewFoodPos.iX = rand() % iWidth;
        stNewFoodPos.iY = rand() % iHight;

        const MapUnitData &stMapUnitData = GetMapUnitData(stNewFoodPos.iX, stNewFoodPos.iY);

        if (stMapUnitData.eType == EM_MAP_UNIT_TYPE_NULL && !IsNear(stNewFoodPos, stHead) )
        {
            MapUnitData &stMutableMapUnitData = MutableMapUnitData(stNewFoodPos.iX, stNewFoodPos.iY);
            stMutableMapUnitData.eType = EM_MAP_UNIT_TYPE_FOOD;
            bCreateFood = true;
            break;
        }
    }

    if (!bCreateFood)
    {
        vector<Pos> vecNullPos;

        //收集空点
        for (int iHightTmp = 0; iHightTmp < iHight; ++iHightTmp)
        {
            for (int iWidthTmp = 0; iWidthTmp < iWidth; ++iWidthTmp)
            {
                const MapUnitData &stMapUnitData = GetMapUnitData(iWidthTmp, iHightTmp);

                Pos stPos;
                stPos.iX = iWidthTmp;
                stPos.iY = iHightTmp;
                if (stMapUnitData.eType == EM_MAP_UNIT_TYPE_NULL && !IsNear(stPos, stHead))
                {
                    vecNullPos.push_back(stPos);
                }
            }
        }
        if (vecNullPos.size() <= 0)
        {
            return false;
        }

        int iRandIndex = rand() % vecNullPos.size();

        stNewFoodPos = vecNullPos[iRandIndex];

        MapUnitData &stMapUnitData = MutableMapUnitData(stNewFoodPos.iX, stNewFoodPos.iY);
        if (stMapUnitData.eType != EM_MAP_UNIT_TYPE_NULL)
        {
            return false;
        }

        stMapUnitData.eType = EM_MAP_UNIT_TYPE_FOOD;
    }

    SetPosition(stNewFoodPos.iX, stNewFoodPos.iY);
    printf(UIChar[EM_MAP_UNIT_TYPE_FOOD]);
    return true;
}

void CFrame::Print(const char *csString)
{
    printf(csString);
#ifndef _WIN32
    fflush(stdout);
#endif
}

bool CFrame::IsNear(const Pos &stSrcPos, const Pos &stDesPos)
{
    if (   (stSrcPos.iX == stDesPos.iX &&  stSrcPos.iY == stDesPos.iY + 1)
        || (stSrcPos.iX == stDesPos.iX &&  stSrcPos.iY == stDesPos.iY - 1)
        || (stSrcPos.iX - 1 == stDesPos.iX &&  stSrcPos.iY == stDesPos.iY)
        || (stSrcPos.iX + 1 == stDesPos.iX &&  stSrcPos.iY == stDesPos.iY))
    {
        return true;
    }
    return false;
}

EMapUnitType CFrame::MoveSnake(EDirType eDir, CSnake &cSnake)
{
    Pos stHeadPos = cSnake.GetSnakeHead();
    Pos stNewPos = stHeadPos;

    switch (eDir)
    {
    case EM_DIR_TYPE_UP:
        --stNewPos.iY;
        break;
    case EM_DIR_TYPE_DOWN:
        ++stNewPos.iY;
        break;
    case EM_DIR_TYPE_LEFT:
        --stNewPos.iX;
        break;
    case EM_DIR_TYPE_RIGHT:
        ++stNewPos.iX;
        break;
    default:
        return EM_MAP_UNIT_TYPE_MAX;
    }

    const EMapUnitType eMapUnitType = GetMapUnitData(stNewPos.iX, stNewPos.iY).eType;
    const int iSnakeLength = cSnake.GetSnakeLength();
    const Pos &stTail = cSnake.GetSnakeTail();

    switch (eMapUnitType)
    {
    case EM_MAP_UNIT_TYPE_WALL:
    case EM_MAP_UNIT_TYPE_SNACKE:
        return eMapUnitType;
    case EM_MAP_UNIT_TYPE_NULL:
    {
        //擦掉蛇的尾巴
        SetPosition(stTail.iX, stTail.iY);
        Print(UIChar[EM_MAP_UNIT_TYPE_NULL]);
        MapUnitData &stMutableMapUnitData = MutableMapUnitData(stTail.iX, stTail.iY);
        stMutableMapUnitData.eType = EM_MAP_UNIT_TYPE_NULL;
        break;
    }
    case EM_MAP_UNIT_TYPE_FOOD:
    {
        //吃到食物
        cSnake.IncreaseSnakeLength();
        break;
    }
    default:
        return EM_MAP_UNIT_TYPE_MAX;
    }

    Pos *pSnake = cSnake.MutableSnake();

    if (pSnake == NULL)
    {
        return EM_MAP_UNIT_TYPE_MAX;
    }

    const int iNewSnakeLength = cSnake.GetSnakeLength();
    for (int iLength = iNewSnakeLength -1; iLength > 0; --iLength)
    {
        pSnake[iLength] = pSnake[iLength-1];
    }

    pSnake[0] = stNewPos;

    MapUnitData &stMutableMapUnitData = MutableMapUnitData(stNewPos.iX, stNewPos.iY);
    stMutableMapUnitData.eType = EM_MAP_UNIT_TYPE_SNACKE;

    
    SetPosition(stNewPos.iX, stNewPos.iY);
    Print(UIChar[EM_MAP_UNIT_TYPE_SNACKE]);

    return eMapUnitType;
}

